/**
 * ReportGear(2011)
 */
package com.reportgear.report.model.cell.style;

import java.awt.Color;
import java.awt.Font;
import java.util.Map;
import java.util.WeakHashMap;

import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.reportgear.core.api.XmlSerializerWithReadReturn;
import com.reportgear.core.swing.util.ColorUtils;
import com.sot.core.util.NumberUtils;

/**
 * 字体样式
 * 
 * @version 1.0 2011-3-10
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 */
public class FontStyle extends Font implements XmlSerializerWithReadReturn<FontStyle> {
	/**
	 * UID
	 */
	private static final long serialVersionUID = -2565844210477509379L;
	// 日志记录
	private static Logger logger = LoggerFactory.getLogger(FontStyle.class.getName());
	/**
	 * 字体样式缓存
	 */
	private static Map<FSK, FontStyle> cachedFontStyle = new WeakHashMap<FSK, FontStyle>();

	/**
	 * 默认字体样式,可设置其它默认样式 如: getInstance("SimSun", 0, 12);
	 */
	public static FontStyle DEFAULT_STYLE = getInstance();

	/**
	 * 字体颜色
	 */
	private Color foreground = Color.BLACK;
	/**
	 * 下划线类型
	 */
	private int underline = 0;
	/**
	 * 穿透线
	 */
	private boolean strikethrough = false;
	/**
	 * 阴影
	 */
	private boolean shadow = false;
	/**
	 * 上标
	 */
	private boolean superscript = false;
	/**
	 * 下标
	 */
	private boolean subscript = false;

	/**
	 * 获取FontStyle实例
	 * 
	 * @return FontStyle实例
	 */
	public static FontStyle getInstance() {
		return getInstance("宋体", 0, 12);
	}

	/**
	 * 获取FontStyle实例
	 * 
	 * @param font
	 *            字体
	 * @return FontStyle实例
	 */
	public static FontStyle getInstance(Font font) {
		return getInstance(font.getName(), font.getStyle(), font.getSize());
	}

	/**
	 * 获取FontStyle实例
	 * 
	 * @param name
	 *            名称
	 * @param style
	 *            类型
	 * @param size
	 *            大小
	 * @return FontStyle实例
	 */
	public static FontStyle getInstance(String name, int style, int size) {
		return getInstance(name, style, size, Color.black, 0, false, false, false, false);
	}

	/**
	 * 获取FontStyle实例
	 * 
	 * @param name
	 *            名称
	 * @param style
	 *            类型
	 * @param size
	 *            大小
	 * @param color
	 *            颜色
	 * @return FontStyle实例
	 */
	public static FontStyle getInstance(String name, int style, int size, Color color) {
		return getInstance(name, style, size, color, 0, false, false, false, false);
	}

	/**
	 * 获取FontStyle实例
	 * 
	 * @param name
	 *            名称
	 * @param style
	 *            类型
	 * @param size
	 *            大小
	 * @param color
	 *            颜色
	 * @param underline
	 *            下划线
	 * @return FontStyle实例
	 */
	public static FontStyle getInstance(String name, int style, int size, Color color, int underline) {
		return getInstance(name, style, size, color, underline, false, false, false, false);
	}

	/**
	 * 获取FontStyle实例
	 * 
	 * @param name
	 *            名称
	 * @param style
	 *            类型
	 * @param size
	 *            大小
	 * @param color
	 *            颜色
	 * @param underline
	 *            下划线
	 * @param strikethrough
	 *            穿透线
	 * @param shadow
	 *            阴影
	 * @param superscript
	 *            上标
	 * @param subscript
	 *            下标
	 * @return FontStyle实例
	 */
	public static FontStyle getInstance(String name, int style, int size, Color color, int underline,
			boolean strikethrough, boolean shadow, boolean superscript, boolean subscript) {
		FSK key = new FSK(name, style, size, color, underline, strikethrough, shadow, superscript, subscript);
		FontStyle fontStyle = cachedFontStyle.get(key);

		if (null != fontStyle) {
			return fontStyle;
		}

		FontStyle newStyle = new FontStyle(name, style, size, color, underline, strikethrough, shadow, superscript,
				subscript);
		cachedFontStyle.put(key, newStyle);

		if (logger.isDebugEnabled()) {
			logger.debug("添加新字体样式，字体样式总数: {}", cachedFontStyle.size());
		}

		return newStyle;
	}

	/**
	 * 应用字体名
	 * 
	 * @param name
	 *            字体名称
	 * @return 字体样式
	 */
	public FontStyle applyName(String name) {
		return getInstance(name, this.style, this.size, this.foreground, this.underline, this.strikethrough,
				this.shadow, this.superscript, this.subscript);
	}

	/**
	 * 应用字体样式
	 * 
	 * @param style
	 *            字体样式
	 * @return 字体样式
	 */
	public FontStyle applyStyle(int style) {
		return getInstance(this.name, style, this.size, this.foreground, this.underline, this.strikethrough,
				this.shadow, this.superscript, this.subscript);
	}

	/**
	 * 应用字体大小
	 * 
	 * @param size
	 *            字体大小
	 * @return 字体样式
	 */
	public FontStyle applySize(int size) {
		return getInstance(this.name, this.style, size, this.foreground, this.underline, this.strikethrough,
				this.shadow, this.superscript, this.subscript);
	}

	/**
	 * 应用字体颜色
	 * 
	 * @param color
	 *            字体颜色
	 * @return 字体样式
	 */
	public FontStyle applyForeground(Color color) {
		return getInstance(this.name, this.style, this.size, color, this.underline, this.strikethrough, this.shadow,
				this.superscript, this.subscript);
	}

	/**
	 * 应用下划线
	 * 
	 * @param underline
	 *            下划线样式
	 * @return 字体样式
	 */
	public FontStyle applyUnderline(int underline) {
		return getInstance(this.name, this.style, this.size, this.foreground, underline, this.strikethrough,
				this.shadow, this.superscript, this.subscript);
	}

	/**
	 * 应用删除线
	 * 
	 * @param strikethrough
	 *            是否删除线
	 * @return 字体样式
	 */
	public FontStyle applyStrikethrough(boolean strikethrough) {
		return getInstance(this.name, this.style, this.size, this.foreground, this.underline, strikethrough,
				this.shadow, this.superscript, this.subscript);
	}

	/**
	 * 应用阴影
	 * 
	 * @param shadow
	 *            是否阴影
	 * @return 字体样式
	 */
	public FontStyle applyShadow(boolean shadow) {
		return getInstance(this.name, this.style, this.size, this.foreground, this.underline, this.strikethrough,
				shadow, this.superscript, this.subscript);
	}

	/**
	 * 应用上标
	 * 
	 * @param superscript
	 *            是否上标
	 * @return 字体样式
	 */
	public FontStyle applySuperscript(boolean superscript) {
		return getInstance(this.name, this.style, this.size, this.foreground, this.underline, this.strikethrough,
				this.shadow, superscript, this.subscript);
	}

	/**
	 * 应用下标
	 * 
	 * @param subscript
	 *            是否下标
	 * @return 字体样式
	 */
	public FontStyle applySubscript(boolean subscript) {
		return getInstance(this.name, this.style, this.size, this.foreground, this.underline, this.strikethrough,
				this.shadow, this.superscript, subscript);
	}

	/**
	 * 
	 * @param name
	 *            名称
	 * @param style
	 *            类型
	 * @param size
	 *            大小
	 * @param color
	 *            颜色
	 * @param underline
	 *            下划线
	 * @param strikethrough
	 *            穿透线
	 * @param shadow
	 *            阴影
	 * @param superscript
	 *            上标
	 * @param subscript
	 *            下标
	 */
	private FontStyle(String name, int style, int size, Color color, int underline, boolean strikethrough,
			boolean shadow, boolean superscript, boolean subscript) {
		super(name, style, size);

		this.foreground = color;
		this.underline = underline;
		this.strikethrough = strikethrough;
		this.shadow = shadow;
		this.superscript = superscript;
		this.subscript = subscript;
	}

	/**
	 * 
	 * @return 字体颜色
	 */
	public Color getForeground() {
		return foreground;
	}

	/**
	 * 
	 * @return 下滑线颜色
	 */
	public int getUnderline() {
		return underline;
	}

	/**
	 * 
	 * @return 是否穿透性
	 */
	public boolean isStrikethrough() {
		return strikethrough;
	}

	/**
	 * 
	 * @return 是否阴影
	 */
	public boolean isShadow() {
		return shadow;
	}

	/**
	 * 
	 * @return 是否上标
	 */
	public boolean isSuperscript() {
		return superscript;
	}

	/**
	 * 
	 * @return 是否下标
	 */
	public boolean isSubscript() {
		return subscript;
	}

	/**
	 * FontStyle键值类，用于缓存FontStyle
	 */
	private static class FSK {
		private String name;
		private int style;
		private int size;
		private Color foreground;
		private int underline;
		private boolean strikethrough;
		private boolean shadow;
		private boolean superscript;
		private boolean subscript;

		FSK(String name, int style, int size, Color foregournd, int underline, boolean strikethrough, boolean shadow,
				boolean superscript, boolean subscript) {
			this.name = name;
			this.style = style;
			this.size = size;
			this.foreground = foregournd;
			this.underline = underline;
			this.strikethrough = strikethrough;
			this.shadow = shadow;
			this.superscript = superscript;
			this.subscript = subscript;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			FSK other = (FSK) obj;
			if (foreground == null) {
				if (other.foreground != null)
					return false;
			} else if (!foreground.equals(other.foreground))
				return false;
			if (name == null) {
				if (other.name != null)
					return false;
			} else if (!name.equals(other.name))
				return false;
			if (shadow != other.shadow)
				return false;
			if (size != other.size)
				return false;
			if (strikethrough != other.strikethrough)
				return false;
			if (style != other.style)
				return false;
			if (subscript != other.subscript)
				return false;
			if (superscript != other.superscript)
				return false;
			if (underline != other.underline)
				return false;
			return true;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((foreground == null) ? 0 : foreground.hashCode());
			result = prime * result + ((name == null) ? 0 : name.hashCode());
			result = prime * result + (shadow ? 1231 : 1237);
			result = prime * result + size;
			result = prime * result + (strikethrough ? 1231 : 1237);
			result = prime * result + style;
			result = prime * result + (subscript ? 1231 : 1237);
			result = prime * result + (superscript ? 1231 : 1237);
			result = prime * result + underline;
			return result;
		}
	}

	/**
	 * 读入样式
	 * 
	 * @param parent
	 * @return
	 */
	public FontStyle read(Element parent) {
		FontStyle fontStyle = this;

		fontStyle = fontStyle.applyName(parent.elementTextTrim("name"));
		fontStyle = fontStyle.applySize(NumberUtils.convertToInteger(parent.elementTextTrim("size"), 12));
		fontStyle = fontStyle.applyStyle(NumberUtils.convertToInteger(parent.elementTextTrim("style"), Font.PLAIN));
		fontStyle = fontStyle.applyForeground(ColorUtils.parseToColor(parent.elementTextTrim("foreground")));
		fontStyle = fontStyle.applyUnderline(NumberUtils.convertToInteger(parent.elementTextTrim("underline")));
		fontStyle = fontStyle.applyStrikethrough("1".equals(parent.elementTextTrim("strikethrough")));
		fontStyle = fontStyle.applyShadow("1".equals(parent.elementTextTrim("shadow")));
		fontStyle = fontStyle.applySuperscript("1".equals(parent.elementTextTrim("superscript")));
		fontStyle = fontStyle.applySubscript("1".equals(parent.elementTextTrim("subscript")));

		return fontStyle;
	}

	public void write(Element parent) {
		Element el;

		el = parent.addElement("name");
		el.addText(this.getName());

		el = parent.addElement("size");
		el.addText(String.valueOf(this.getSize()));

		el = parent.addElement("style");
		el.addText(String.valueOf(this.getStyle()));

		el = parent.addElement("foreground");
		el.addText(ColorUtils.toHexEncoding(this.foreground));

		el = parent.addElement("underline");
		el.addText(String.valueOf(this.underline));

		el = parent.addElement("strikethrough");
		el.addText(this.strikethrough ? "1" : "0");

		el = parent.addElement("shadow");
		el.addText(this.shadow ? "1" : "0");

		el = parent.addElement("superscript");
		el.addText(this.superscript ? "1" : "0");

		el = parent.addElement("subscript");
		el.addText(this.subscript ? "1" : "0");
	}
}
